package com.chinatelecom.framework.generic;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chinatelecom.framework.model.PageData;
import com.chinatelecom.framework.model.PageParam;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Date;
import java.util.List;

/**
 * GenericService的实现类, 其他的自定义 ServiceImpl, 继承自它,可以获得常用的增删查改操作, 未实现的方法有 子类各自实现
 */
public abstract class GenericServiceImpl<T> implements GenericService<T> {

	/**
	 * 日志对象
	 */
	protected Logger logger = LogManager.getLogger(this.getClass());

	/**
	 * mysql持久层对象
	 */
	@Autowired(required = false)
	protected GenericDao<T> dao;

	/**
	 * 通过反射,获得定义Class时声明的父类的范型参数的类型. 如public BookManager extends GenricManager<Book>
	 * 
	 * @param clazz clazz The class to introspect
	 * @param index the Index of the generic ddeclaration,start from 0.
	 */
	public Class<Object> getSuperClassGenricType(Class<?> clazz, int index) throws IndexOutOfBoundsException {
		Type genType = clazz.getGenericSuperclass();
		if (!(genType instanceof ParameterizedType)) {
			return Object.class;
		}
		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
		if (index >= params.length || index < 0) {
			return Object.class;
		}
		if (!(params[index] instanceof Class)) {
			return Object.class;
		}
		return (Class<Object>) params[index];
	}

	/**
	 * 设置更新人员
	 */
	private void setUpdateInfo(T entity, Integer updateBy) {
		Class<? extends Object> cls = entity.getClass();
		Method setUpdateDate;
		Method setUpdateBy;
		try {
			setUpdateDate = cls.getDeclaredMethod("setUpdateTime", Date.class);
			if (setUpdateDate != null) {
				setUpdateDate.invoke(entity, new Date());
			}

			if (updateBy != null) {
				setUpdateBy = cls.getDeclaredMethod("setUpdateBy", String.class);
				if (setUpdateBy != null) {
					setUpdateBy.invoke(entity, updateBy);
				}
			}

		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 设置创建人员
	 */
	private void setCreateInfo(T entity, Integer createBy) {
		Class<? extends Object> cls = entity.getClass();
		Method setCreateDate;
		Method setCreateBy;
		try {
			// cn.smarthse.business.entity.system.SysLog.setCreateTime(Date)
			setCreateDate = cls.getDeclaredMethod("setCreateTime", Date.class);
			if (setCreateDate != null) {
				setCreateDate.invoke(entity, new Date());
			}

			// 设置有效状态
			setIsValid(entity, true);

			if (createBy != null) {
				setCreateBy = cls.getDeclaredMethod("setCreateBy", String.class);
				if (setCreateBy != null) {
					setCreateBy.invoke(entity, createBy);
				}
			}

		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 设置isvalid
	 * 
	 */
	private void setIsValid(T entity, boolean isvalid) {
		Class<? extends Object> cls = entity.getClass();
		Method setIsValid;

		try {
			setIsValid = cls.getDeclaredMethod("setIsValid", Boolean.class);
			if (setIsValid != null) {
				setIsValid.invoke(entity, isvalid);
			}
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

	private Integer getUid() {
//		ShiroPrincipal p = null;
//		Object obj = SecurityUtils.getSubject().getPrincipal();
//		p = (ShiroPrincipal) obj;
//		Integer uid = p.getUser().getId();
		return 0;
	}

	/**
	 * 插入对象
	 */
	public T insert(T record) {
		this.setCreateInfo(record, getUid());
		this.setUpdateInfo(record, getUid());
		this.setIsValid(record, true);
		dao.insert(record);
		return record;
	}

	/**
	 * 更新对象
	 *
	 * @param record 对象
	 */
	public int update(T record) {
		this.setUpdateInfo(record, getUid());
		dao.updateById(record);
		return 1;
	}

	/**
	 * 通过主键, 删除对象
	 *
	 * @param id 主键
	 */
	public int delete(Object id) {
		// 提交更新
		T entity = this.getById(id);
		setIsValid(entity, false);
		setUpdateInfo(entity, getUid());
		dao.updateById(entity);
		return 1;
	}

	/**
	 * 通过主键, 查询对象
	 *
	 * @param id 主键
	 * @return 对象
	 */
	public T getById(Object id) {
		return dao.selectById((Serializable) id);
	}

	/**
	 * 通过主键, 查询对象
	 *
	 * @param idList 主键列表
	 * @return 对象列表
	 */
	public List<T> getByIds(Collection<? extends Serializable> idList) {
		return dao.selectBatchIds(idList);
	}

	/**
	 * 根据条件查询列表数据
	 * 
	 * @param queryWrapper
	 * @return
	 */
	public T getOne(Wrapper<T> queryWrapper) {
		return dao.selectOne(queryWrapper);
	}

	/**
	 * 根据条件查询列表数据
	 * 
	 * @param queryWrapper
	 * @return
	 */
	public List<T> getList(Wrapper<T> queryWrapper) {
		return dao.selectList(queryWrapper);
	}

	/**
	 * 根据条件查询列表数据(分页)
	 * 
	 * @param page         分页条件
	 * @param queryWrapper 查询条件
	 * @return
	 */
	public IPage<T> getPage(IPage<T> page, Wrapper<T> queryWrapper) {
		return dao.selectPage(page, queryWrapper);
	}


	@Override
	public PageData<T> getPageData(PageParam param) {
		QueryWrapper wrapper = new QueryWrapper();
		IPage page = new Page(param.getPage(), param.getPageSize());
		IPage list = this.getPage(page, wrapper);
		PageParam pageParam = new PageParam();
		pageParam.setPage(param.getPage());
		pageParam.setPageSize(param.getPageSize());
		PageData data = new PageData(list, pageParam);
		return data;
	}
}
